package com.youlu.campus.web.config.filter;

import com.youlu.campus.base.user.enums.UserStatusEnum;
import com.youlu.campus.common.base.QResult;
import com.youlu.campus.common.base.enums.CodeEnum;
import com.youlu.campus.common.constants.RequestHeaderConstant;
import com.youlu.campus.entity.UserPO;
import com.youlu.campus.service.utils.WebTokenUtils;
import com.youlu.campus.web.common.base.RequestContext;
import com.youlu.campus.web.config.filter.utils.ResponseUtils;
import com.youlu.campus.web.user.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 认证 过滤器
 *
 * @Description: AuthFilter
 * @Date 2025/7/21
 * @author: su
 */
@Slf4j
public class AuthFilter extends OncePerRequestFilter {

    /**
     * servlet path 路径前缀
     */
    private final List<String> servletPaths;

    /**
     * 不拦截的url
     */
    private final List<AuthExcludedUrl> excludedUrl;


    private final RedisTemplate<String, String> redisTemplate;

    private final UserService userService;

    public AuthFilter(List<String> servletPaths, List<String> excludeUrl, RedisTemplate<String, String> redisTemplate, UserService userService) {
        this.servletPaths = servletPaths;
        this.redisTemplate = redisTemplate;
        this.userService = userService;
        if (!CollectionUtils.isEmpty(excludeUrl)) {
            List<AuthExcludedUrl> arrayList = new ArrayList(excludeUrl.size());
            for (String url : excludeUrl) {
                arrayList.add(AuthExcludedUrl.of(url));
            }
            this.excludedUrl = arrayList;
        } else {
            this.excludedUrl = null;
        }
    }


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String miniToken = request.getHeader(RequestHeaderConstant.MINI_TOKEN);
        String platform = request.getHeader(RequestHeaderConstant.PLATFORM);
        String appId = request.getHeader(RequestHeaderConstant.APP_ID);
        String authorizeMode = request.getHeader(RequestHeaderConstant.AUTHORIZE_MODE);
        String requestUrl = getRequestUrl(request);
        boolean exclude = isExclude(platform, requestUrl, miniToken);
        if (exclude) {
            log.debug("AuthFilter request exclude requestUrl:{},miniToken:{},appId:{},platform:{}", requestUrl, miniToken, appId, platform);
            filterChain.doFilter(request, response);
            return;
        }
        log.info("AuthFilter url:{}, miniToken:{}, platform:{}, appId:{},", requestUrl, miniToken, platform, appId);
        if (StringUtils.isBlank(miniToken)) {
            log.error("AuthFilter miniToken is null");
            ResponseUtils.writeOutJson(response, QResult.fail(CodeEnum.USER_NOT_LOGIN));
            return;
        }
        String openId = WebTokenUtils.getOpenIdByMiniToken(miniToken);
        if (StringUtils.isBlank(openId)) {
            log.error("AuthFilter openId is null");
            ResponseUtils.writeOutJson(response, QResult.fail(CodeEnum.USER_NOT_LOGIN));
            return;
        }

        //获取用户信息
        UserPO user = getUserInfo(miniToken);
        if (Objects.isNull(user)) {
            log.info("AuthFilter user is null miniToken:{},platform:{},appId:{},url:{}", miniToken, platform, appId, requestUrl);
            ResponseUtils.writeOutJson(response, QResult.fail(CodeEnum.USER_NOT_LOGIN));
            return;
        }
        if (!UserStatusEnum.NORMAL.getValue().equals(user.getStatus())) {
            log.error("AuthFilter user status error userId:{},status:{}", user.getId(), user.getStatus());
            ResponseUtils.writeOutJson(response, QResult.fail());
            return;
        }
        log.info("AuthFilter success userId:{},url:{}", user.getId(), requestUrl);
        //设置上下文
        setContext(user, platform, appId, authorizeMode);

        filterChain.doFilter(request, response);
    }

    protected UserPO getUserInfo(String miniToken) {
        String userId = redisTemplate.opsForValue().get("user_token:" + miniToken);
        if (StringUtils.isNotBlank(userId)) {
            return userService.queryUserById(userId);
        }
        return null;
    }

    /**
     * 获取请求url
     *
     * @param req
     * @return
     */
    public String getRequestUrl(HttpServletRequest req) {
        String requestUrl = req.getServletPath();
        if (!CollectionUtils.isEmpty(servletPaths)) {
            for (String servletPath : servletPaths) {
                if (requestUrl.startsWith(servletPath)) {
                    return requestUrl.replace(servletPath, "");
                }
            }
        }
        return requestUrl;
    }

    /**
     * 是否排除请求
     *
     * @param platform
     * @param url
     * @return
     */
    public boolean isExclude(String platform, String url, String miniToken) {
        //如果 传了TOKEN 那么就去获取用户信息
        if (StringUtils.isNotBlank(miniToken)) {
            return false;
        }

        if (!CollectionUtils.isEmpty(excludedUrl)) {
            for (AuthExcludedUrl excludeUrl : excludedUrl) {
                if (excludeUrl.isExclude(url)) {
                    return true;

                }
            }
        }
        return false;
    }

    /**
     * 设置上下文
     *
     * @param userInfo
     * @param platform
     * @param appId
     * @param authorizeMode
     */
    public void setContext(UserPO userInfo, String platform, String appId, String authorizeMode) {
        //清空上下文
        RequestContext.clear();
        userInfo.setCurrentPlatform(platform);
        RequestContext.setCurUser(userInfo);
        RequestContext.setCurPlatform(platform);
        RequestContext.setCurAppId(appId);
        RequestContext.setCurAuthorizeMode(authorizeMode);
    }
}
